package javaproject;

import com.sun.j3d.utils.geometry.Sphere;
import java.util.Iterator;
import java.util.LinkedList;
import javax.media.j3d.*;
import javax.vecmath.Color3f;

class Bullet extends MyPrimitive{
    public static int NORMAL_BULLET = 0;
    public static int FAST_BULLET = 1;
    public static int HOMING_BULLET = 2;
    public static int CLUSTER_BULLET = 3;
    public static int RANDOM_BULLET = 4;
    private Debugger debugger;
    private Sphere sphere;
    private Sphere bigSphere;
    private BranchGroup sphereGroup;
    private BranchGroup bigSphereGroup;
    private BranchGroup container;
    private double travaledDistance;
    public boolean exploded;
    public double vx,vy,vz;
    public int type;
    Bullet(double x, double y, double z, double vx, double vy, double vz, Appearance app, Debugger debugger)
    {
        super(x,y,z,app);
        setV(vx,vy,vz);
        this.type = type;
        this.debugger = debugger;
        this.travaledDistance = 0;
        this.exploded = false;

        transG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        
        container = new BranchGroup();
        container.setCapability(Group.ALLOW_CHILDREN_WRITE);
        container.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

        sphereGroup = new BranchGroup();
        sphereGroup.setCapability(BranchGroup.ALLOW_DETACH);
        sphere = new Sphere(Config.bulletSize, app);
        sphere.getShape().setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
        sphereGroup.addChild(sphere);
        
        bigSphereGroup = new BranchGroup();
        bigSphereGroup.setCapability(BranchGroup.ALLOW_DETACH);
        bigSphere = new Sphere(Config.bulletSize*2, app);
        bigSphere.getShape().setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
        bigSphereGroup.addChild(bigSphere);

        container.addChild(sphereGroup);
        container.addChild(bigSphereGroup);
        transG.addChild(container);
    }
    public void randomize(double targetX, double targetY, double targetZ)
    {
        double radA, radB;
        double x,y,z,r;
        double vx,vy,vz;

        radA=(Math.random()*Math.PI*2); //A 角度  [0,2PI)
        //radB=(Math.random()*Math.PI);    //B  角度 [0,PI)
        radB=Math.PI/2;                       //只有2D

        //起始位置
        x=Config.radius*Math.cos(radA)*Math.sin(radB);
        y=Config.radius*Math.sin(radA)*Math.sin(radB);
        //z=Config.radius*Math.cos(radB);
        z=0;

        //  單位向量
        vx=targetX-x+(Math.random()-0.5)*Config.offsetFactor;
        vy=targetY-y+(Math.random()-0.5)*Config.offsetFactor;
        vz=0;
        r = Math.sqrt(vx*vx+vy*vy+vz*vz);
        vx = vx/r * Config.forwardStep;
        vy = vy/r * Config.forwardStep;
        vz = vz/r * Config.forwardStep;

        if(type == FAST_BULLET)
        {
            vx *= 2;
            vy *= 2;
            vz *= 2;
        }
        else if(type == CLUSTER_BULLET)
        {
            vx /= 2;
            vy /= 2;
            vz /= 2;
        }
        setV(vx, vy, vz);
        moveTo(x, y, z);
    }
    public void randomize()
    {
        randomize(0,0,0);
    }
    public void setV(double[] v)
    {
        setV(v[0], v[1], v[2]);
    }
    public void setV(double vx, double vy, double vz)
    {
        this.vx = vx;
        this.vy = vy;
        this.vz = vz;
    }
    public void forward(double targetX, double targetY, double targetZ)
    {
        if(type == HOMING_BULLET)
        {
            double r = Math.sqrt((targetX-x)*(targetX-x)+(targetY-y)*(targetY-y)+(targetZ-z)*(targetZ-z));
            //單位向量
            double vectorX=(targetX-x)/r;
            double vectorY=(targetY-y)/r;
            double vectorZ=0;
            
            //改變速度的factor
            double factor = 0.007;
            double threshold = Config.boundingW/5;
            if(r > threshold)
                factor = factor*(1-Math.pow((r-threshold)/Config.radius,0.3));
            
            vx += vectorX*factor*Config.forwardStep;
            vy += vectorY*factor*Config.forwardStep;
            vz += vectorZ*factor*Config.forwardStep;
            
            double r2 = Math.sqrt(vx*vx+vy*vy+vz*vz);
            vx = vx/r2*Config.forwardStep;
            vy = vy/r2*Config.forwardStep;
            vz = vz/r2*Config.forwardStep;
            
            move(vx,vy,vz);
        }
        else if(type == CLUSTER_BULLET)
        {
            double distance = Math.sqrt(x*x+y*y+z*z);
            travaledDistance += distance;
            if(distance < Config.explodeDistance)
            {
                exploded = true;
            }
            move(vx,vy,vz);
        }
        else 
        {
            move(vx,vy,vz);
        }
    }
    @Override
    public void setAppearance(Appearance aprnc) {
        app = aprnc;
        sphere.getShape().setAppearance(app);
        bigSphere.getShape().setAppearance(app);
    }
    public void useBigSphere()
    {
        container.removeChild(bigSphereGroup);
        container.removeChild(sphereGroup);
        container.addChild(bigSphereGroup);
    }
    public void useSphere()
    {
        container.removeChild(bigSphereGroup);
        container.removeChild(sphereGroup);
        container.addChild(sphereGroup);
    }
}
class Bullets
{
    public LinkedList<Bullet> bulletList;
    public LinkedList<Bullet> bulletTemp;
    public Appearance app;
    public Appearance appRed;
    public Appearance appYellow;
    public Appearance appGreen;
    private BranchGroup objContainer;
    private Debugger debugger;
    Bullets(BranchGroup objContainer, Debugger debugger)
    {
        bulletList = new LinkedList<Bullet>();
        bulletTemp = new LinkedList<Bullet>();
        this.objContainer = objContainer;
        this.debugger = debugger;
    }
    public void putin(Bullet b)
    {
        if(!bulletList.contains(b))
        {
            bulletList.add(b);
            b.randomize();
        }
    }
    public void putin(Bullet b, double[] pos)
    {
        if(!bulletList.contains(b))
        {
            bulletList.add(b);
            b.randomize(pos[0], pos[1], pos[2]);
        }
    }
    public void initBullets(BranchGroup TG)
    {
        Material mat = new Material();
        mat.setEmissiveColor(Config.bulletColor);
        app = new Appearance();
	app.setMaterial(mat);
        
        Material matRed = new Material();
        matRed.setEmissiveColor(1,0,0);
        appRed = new Appearance();
	appRed.setMaterial(matRed);
        
        Material matYellow = new Material();
        matYellow.setEmissiveColor(1,1,0);
        appYellow = new Appearance();
	appYellow.setMaterial(matYellow);
        
        Material matGreen = new Material();
        matGreen.setEmissiveColor(0,1,0);
        appGreen = new Appearance();
	appGreen.setMaterial(matGreen);

        for(int i=0;i<Config.initBullets;i++)
        {
            Bullet b = new Bullet(0,0,0,0,0,0,app, debugger);
            //Bullet b = new Bullet(0,0,0,0,0,0,appGreen,Bullet.CLUSTER_BULLET, debugger);
            changeBulletType(b, Bullet.NORMAL_BULLET);
            //Bullet b = new Bullet(0,0,0,0,0,0,app,Bullet.HOMING_BULLET);
            TG.addChild(b.getTransformGroup());
            putin(b);
        }
    }
    public void addBullets(int num, double[] pos, double[] targetPos, int type, boolean randomize)
    {
        Bullet b;
        for(int i = 0; i < num; i++)
        {
            if(bulletList.size()+bulletTemp.size() < Config.maxBullets)
            {
                b = new Bullet(pos[0],pos[1],pos[2],targetPos[0],targetPos[1],targetPos[2],app,debugger);
                changeBulletType(b, type);
                
                BranchGroup bg = new BranchGroup();
                bg.addChild(b.getTransformGroup());
                bg.compile();
                objContainer.addChild(bg);
                
                bulletTemp.add(b);
                
                if(randomize)
                    b.randomize(targetPos[0],targetPos[1],targetPos[2]);
            }
        }
    }
    public void addBullets(int num, double[] pos, int type, boolean randomize)
    {
        addBullets(num, pos, new double[]{0,0,0}, type, randomize);
    }
    public void proceed(double[] targetPos)
    {
        boolean clusterExplode = false;
        
        Iterator it = bulletList.iterator();
        while(it.hasNext())
        {
            Bullet b = (Bullet)it.next();
            //跑出畫面外
            if(Math.sqrt(b.x*b.x + b.y*b.y + b.z*b.z) > Config.radius)
            {
                //子彈變化的機率
                if(Math.random() < Config.mutateFactor)
                {
                    double r = Math.random();
                    if(r > 0.5)
                    {
                        changeBulletType(b, Bullet.HOMING_BULLET);
                    }
                    else if(r > 0.1)
                    {
                       changeBulletType(b, Bullet.FAST_BULLET);
                    }
                    else
                    {
                        changeBulletType(b, Bullet.CLUSTER_BULLET);
                    }
                }
                else
                {
                    changeBulletType(b, Bullet.NORMAL_BULLET);
                }
                b.randomize(targetPos[0], targetPos[1], targetPos[2]);
            }
            else if(b.exploded)
            {
                b.exploded = false;
                clusterExplode = true;
                changeBulletType(b, Bullet.NORMAL_BULLET);
                
                addBullets(8,new double[]{b.x,b.y,b.z},Bullet.NORMAL_BULLET, false);

                b.randomize();
            }
            else
                b.forward(targetPos[0], targetPos[1], targetPos[2]);
        }
        
        if(!clusterExplode)
        {
            Iterator it_t = bulletTemp.iterator();
            while(it_t.hasNext())
            {
                Bullet b = (Bullet)it_t.next();
                bulletList.add(b);
            }
        }
        else
        {
            for(int i = 0; i < bulletTemp.size()/8; i++)
            {
                Iterator it_t = bulletTemp.iterator();
                Bullet b;
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.UP));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.DOWN));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.LEFT));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.RIGHT));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.UP_LEFT));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.UP_RIGHT));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.DOWN_LEFT));
                bulletList.add(b);
                b = (Bullet)it_t.next();
                b.setV(Snake.dirToVel(Snake.DOWN_RIGHT));
                bulletList.add(b);
            }            
        }
        bulletTemp.clear();
    }
    public void proceed()
    {
        proceed(new double[]{0,0,0});
    }
    private void changeBulletType(Bullet b, int type)
    {
        b.type = type;
        if(type == Bullet.HOMING_BULLET)
        {
            b.setAppearance(appRed);
            b.useSphere();
        }
        else if(type == Bullet.FAST_BULLET)
        {
            b.setAppearance(appYellow);
            b.useSphere();
        }
        else if(type == Bullet.CLUSTER_BULLET)
        {
            b.setAppearance(appGreen);
            b.useBigSphere();
        }
        else
        {
            b.setAppearance(app);
            b.useSphere();
        }
    }
}
